home *** CD-ROM | disk | FTP | other *** search
- #include "datapriv.hpp"
-
- /*
- This file contains the routines concerned with the generation and use
- of indclus structures and indpt structures
- */
-
- /**********************************************************************************
-
- INDPT Routines
-
- ***************************************************************************/
-
- // Constructor, call with cluster number, parent and index
-
- indpt::indpt(long nclus,indpt *iparent,index *oi)
- {
- currec=-1;
- parent=iparent;
- indclus *pi=(parent) ? parent->icp : 0;
-
- indclus *ip;
-
- ip=oi->topind;
- while(ip)
- {
- if ((ip->clusn==nclus) && ip->valid) break;
- ip=ip->next;
- }
-
- if (ip)
- {
- icp=ip; // Set pointer to existing buffer
- if (parent) ip->parent=parent->icp; // Update parent - may have changed
- else ip->parent=0;
- ip->nuse++;
- }
- else
- {
-
- // Here if we have more clusters in memory than MAXCLUSM, then
- // run thru all clusters, invalid clusters have higher priority
- // for deletion than record clusters, which have higher priority
- // than pointer clusters
-
- if (oi->nclus>MAXCLUSM) // Look for clusters to delete
- {
- indclus *dip=0;
- int diptype=0; // 0=all, 1=pointer, 2=record
-
- ip=oi->topind;
- while(ip)
- {
- if (!ip->nuse)
- {
- if (!ip->valid) {dip=ip; break;}
- if (diptype<3 && !ip->clustyp) {dip=ip; diptype=2;}
- if (diptype<2 && ip->clustyp) {dip=ip; diptype=1;}
- }
- ip=ip->next;
- }
- if (dip && (dip!=oi->topind)) delete dip;
- }
- icp=new indclus(nclus,pi,oi);
- icp->nuse++;
- }
- }
-
- // Construct with pointer to indclus
-
- indpt::indpt(indclus *ip)
- {
- icp=ip;
- currec=-1;
- parent=0;
- icp->nuse++;
- }
-
- // Destructor, check if indclus is in use by someone else, otherwise delete
-
- indpt::~indpt()
- {
- icp->nuse--;
-
- // delete invalid or record clusters now
-
- if (!(icp->nuse) && (icp!=icp->oi->topind) &&
- (!(icp->valid) || !(icp->clustyp))) delete icp;
- }
-
- /******** Select a record by key value in indpt ***************************/
-
- // Call with a top level level indpt, constructs a tree to the nearest
- // record, and returns with at pointer to the end of the tree.
- // containing the record.
- // num is OPINT or OPSTR for string or double result
- // ret is 0 if found, or the error code if not
- // i is the number of the record
-
- indpt *indpt::selkey(char *ivalue,int num,int &ret,int &i)
- {
- char value[128],ws[16];
- if (num!=OPINT) {strncpy(value,ivalue,120); value[icp->explen]=0;}
- else *(double *)value=*(double *)ivalue;
- indpt *curind=this;
- int lnum=curind->icp->oi->restype;
- if (lnum==OPDATE) value[8]=0; // Chop off unwanted bits of date
-
- currec=-1; ret=-1;
-
- if (!icp->nclusrec) {ret=NOKEY; i=0; return this;} // 0 length index
-
- // Now in each cluster search for the record greater than or equal to value
- do
- {
- double cr; // compare result
- int re=0; // Flag reached end of cluster with this record
- int rchk; // No. of records to check in this cluster
-
- rchk=curind->icp->nclusrec-curind->icp->clustyp;
-
- char *keyp=curind->icp->clusdat+12;
-
- for(i=0; i<rchk; i++)
- {
- if (lnum==OPINT) cr=*(double *)keyp-*(double *)value;
- if (lnum==OPSTR) cr=strcmpdb(keyp,value,curind->icp->explen);
- if (lnum==OPDATE) {strcpy(ws,keyp); ws[8]=0; cr=atol(ws)-atol(value);}
- if (cr>=0) break;
-
- keyp+=icp->reclen;
- }
-
- if (i==curind->icp->nclusrec) {i--; re=1;} // Reached end, set to last
- curind->currec=i; // Set next record to be read
-
- if (!curind->icp->clustyp) // Down to the nearest key so quit
- {
- if (!cr) ret=0; else ret=(re) ? NOKEYHIGHER : NOKEY;
- }
- else
- {
- curind=new indpt(*(long *)
- (curind->icp->clusdat+4+i*curind->icp->reclen),curind,
- curind->icp->oi);
- }
- }
- while(ret==-1);
-
- return curind;
- }
-
- /***************************************************************
-
- INDCLUS Routines
-
- ***************************************************************/
-
- // Constructor, call with cluster number, parent and index
- // if iclusn the cluster no. is -1 then create a new cluster at
- // the end of the file
-
- indclus::indclus(long iclusn,indclus *iparent,index *iip)
- {
- oi=iip;
- fp=oi->fp;
- parent=iparent;
- clusn=iclusn;
- change=0;
- next=0;
- prev=0;
- nuse=0;
- valid=1;
- oi->nclus++;
-
- if (clusn==-1) // New cluster
- {
- clusn=oi->lblk++;
- oi->chng=1;
- change=1;
- clustyp=0;
- nclusrec=0;
- memset(clusdat,0,512);
- }
- else // Read existing cluster
- {
- Fseek(fp,iclusn*512L,SEEK_SET);
- Fread(clusdat,1,512,fp);
- nclusrec=*(int *)(clusdat);
- clustyp=(*(long *)(clusdat+4)>0L) ? 1 : 0;
- if (clustyp) nclusrec++;
- }
-
- if (oi->topind)
- {
- reclen=oi->topind->reclen;
- explen=oi->topind->explen;
- next=oi->topind->next;
- prev=oi->topind;
- oi->topind->next=this;
- if (next) next->prev=this;
-
- }
- }
-
- // Destructor, clean up pointers, write to disk if changed
-
- indclus::~indclus()
- {
- if (next) next->prev=prev;
- if (prev) prev->next=next;
- if (oi->topind==this) oi->topind=next;
- if (change && valid)
- { Fseek(fp,clusn*512L,SEEK_SET); Fwrite(clusdat,1,512,fp); }
- oi->nclus--;
- }
-
- // Subtract record n from a cluster, if the cluster becomes 0 length
- // then delete it and move round the file
-
- void indclus::subtract(int cr)
- {
- if (nclusrec>1) // Cluster still has records
- {
- int dst=4+cr*reclen; // Points to key to be removed
- int src=dst+reclen; // Points past key to be removed
- int nb=512-src; // No. of bytes to move
- memmove(clusdat+dst,clusdat+src,nb); // Move the data
- nclusrec--;
- (*((long *)clusdat))--;
- change=1;
-
- // Now we check all parents to see if we need to change the last record
- // key as a result of subtracting this one.
-
- indclus *ci=this;
- cr--; // Reduce record number
- while (cr==ci->nclusrec-1 && ci->parent)
- {
- char *ptr=ci->parent->clusdat+4;
- while(*(long *)ptr!=ci->clusn) ptr+=reclen;
- ptr+=8;
- memmove(ptr,ci->clusdat+12+cr*reclen,explen);
- ci->parent->change=1;
- cr=((ptr-ci->parent->clusdat)-12)/reclen; // record number in parent
- ci=ci->parent;
- }
- return;
- }
-
- // Now there are NO records left in this cluster
-
- if (!parent) // Very top of tree so don't delete this record !
- {
- memset(clusdat,0,512);
- *(long *)clusdat=0-clustyp; // Indicate no records at top of tree
- nclusrec=0;
- change=1;
- return;
- }
-
- // Remove this cluster from its parent
-
- indclus *pp=parent;
- int rn=0; // Find key of this cluster in parent
-
- char *ptr=pp->clusdat+4;
- while(*(long *)ptr!=clusn) {ptr+=reclen; rn++;}
- pp->subtract(rn);
-
- // Maybe we are deleting the last block, if so don't move anything
-
- if (clusn==oi->lblk-1) {oi->lblk--; oi->chng=1; return;}
-
- // Now move the last block into the space occupied by this
-
- indpt bm(oi->lblk-1,0,oi); // Block to move
- valid=0; // Discard this block
-
- indpt *pbl=bm.icp->findptr(rn); // Find pointer to block
-
- if (pbl)
- {
- *(long *)(pbl->icp->clusdat+4+reclen*rn)=clusn; // Readjust cluster
- pbl->icp->change=1;
-
- indpt *npt;
- while(pbl->parent) {npt=pbl->parent; delete pbl; pbl=npt;} delete pbl;
- }
- else // Moved block is the top of the tree
- {
- oi->tblk=clusn;
- }
-
- bm.icp->clusn=clusn;
- bm.icp->change=1; // Flag bm to be written to disk
- oi->lblk--;
- oi->chng=1;
- }
-
- // Find the cluster which points at the supplied block, and
- // construct a tree to it, return the tree, or 0 if the supplied cluster
- // is the top block, note the indclus which calls this routine does
- // not have to be part of the tree structure
- // rn returns the record number in the block
-
- indpt *indclus::findptr(int &rn)
- {
- if (clusn==oi->tblk) return 0; // Top block
-
- int dum,dum2;
- indpt *bt=0; // Bottom of tree after search
- char *pt; // Pointer to record numbers
- indpt *nt; // Useful pointer
-
- void *val=clusdat+12; // First value in block
- indpt *fi=new indpt(oi->topind); // fi is the top index
-
- bt=fi->selkey((char *)val,oi->indtype,dum,dum2); // Get key matching 1st item
- if (!bt->parent) {delete fi; return 0;}
-
- while(1)
- {
- if (bt->icp->clustyp) // Ignore a record cluster, go back to parent
- {
- pt=bt->icp->clusdat+4+(bt->currec)*reclen; // Next rec. no.
- if (*(long *)pt==clusn) break; // Found It !
- }
- nt=bt->parent; delete bt; bt=nt; // Get parent record
- if (!bt) {dbfer(NODELKEY); break;} // At top ? then error
- }
-
- rn=bt->currec;
- return bt;
- }
-
- // Add a record to the index file
- // Call with the right cluster, if we can insert a record do so,
- // if not split the current cluster and adjust the parents
-
- // newkey is the key to be added
- // rn is the record no. associated with the key
- // irn is the no. of the next highest record in the cluster, or the
- // next lower if rsk is NOKEYHIGHER (ie the key is the last to be added)
-
- // Returns index of new cluster if it exists, or 0 if none
-
- int indclus::add(char *newkey,long rn,int irn,int rsk)
- {
- change=1; // Well we are going to change this cluster
-
- if (nclusrec<oi->maxclusrec) // We can fit in an extra record
- {
- int src,dst,nb; // source, destination & no. of bytes
-
- nclusrec++; (*((long *)clusdat))++; // New no. of records
-
- if (rsk==NOKEYHIGHER) // Add to end of this one
- {
- src=(irn+1)*reclen+4; // Move to end
- *(long *)(clusdat+src+reclen)=0; // Clear following
- if (parent) parent->newkey(clusn,newkey,clusn);
- }
- else
- {
- src=irn*reclen+4; // Where to make space
- dst=src+reclen; // Where to move up
- nb=512-dst; // No. of bytes
- memmove(clusdat+dst,clusdat+src,nb); // Move up
- }
- memset(clusdat+src,0,8); // Clear existing rubbish
- *(long *)(clusdat+src+4*(1-clustyp))=rn; // Write in record number
- memmove(clusdat+src+8,newkey,explen); // Copy key
- return 0;
- }
-
- // Now we have to insert a new block
-
- indclus newclus(-1,0,oi); // Create a new block full of 0's
- newclus.clustyp=clustyp; // Same cluster type as this
- int nrn=nclusrec/2; // New no. of records in this cluster
- int nnew=nclusrec-nrn; // No. of records in the new cluster
- int src=4+nrn*reclen; // Location to chop cluster in half
-
- memmove(newclus.clusdat+4,clusdat+src,512-src); // Copy
- memset(clusdat+src,0,8); // Clear end
- *(long *)(clusdat)=nrn-clustyp; nclusrec=nrn;
- *(long *)(newclus.clusdat)=nnew-clustyp; newclus.nclusrec=nnew;
- char *last=clusdat+src-reclen+8;
-
- // Now find location in parent to insert the new block
-
- char *newlast=newclus.clusdat+(newclus.nclusrec-1)*reclen+12;
-
- if (parent)
- {
- int rv;
- int i=parent->newkey(clusn,newlast,newclus.clusn); // Set newlast in parent
- rv=parent->add(last,clusn,i,0); // Add record before new one
- }
- else // We have split the top block, make a new one
- {
- indclus *newparent=new indclus(-1,0,oi); // New top block
- newparent->clustyp=1; // Pointer block
- newparent->add(last,clusn,-1,NOKEYHIGHER); // add this block &
- newparent->add(newlast,newclus.clusn,0,NOKEYHIGHER); // add the new block
- newparent->nclusrec=2;
- *(long *)(newparent->clusdat)=1; // update no. of records
- oi->tblk=newparent->clusn;
- oi->chng=1;
-
- // Now link in the new top block, we know that this record is the
- // old top block, and newparent is the new one
-
- if (newparent->next) newparent->next->prev=newparent->prev;
- if (newparent->prev) newparent->prev->next=newparent->next;
-
- oi->topind=newparent;
- newparent->prev=0;
- newparent->next=this;
- prev=newparent;
- parent=newparent;
- newclus.parent=newparent;
- }
-
- // Finally insert the new record
-
- if (irn>=nclusrec) // Insert key in new cluster ?
- {
- if (irn==nclusrec) newclus.add(newkey,rn,0,0);
- else
- {
- irn-=nclusrec; // New position to insert key
- newclus.add(newkey,rn,irn,rsk); // Insert in new cluster
- }
- }
- else // Insert key in this cluster
- {
- if (irn==nclusrec) add(newkey,rn,irn-1,NOKEYHIGHER);
- else add(newkey,rn,irn,0);
- }
- return newclus.clusn;
- }
-
- // Change key value of pointer record in cluster to the supplied
- // new key for the supplied cluster no. also change cluster no if different
- // Return number of altered record
-
- int indclus::newkey(long kclus,char *newkey,long newclus)
- {
- char *cp=clusdat+4;
- int i=0;
-
- while(*(long *)cp!=kclus) {cp+=reclen; i++;}
-
- *(long *)cp=newclus;
- memmove(cp+8,newkey,explen);
- change=1;
-
- // Now we check parent to see if we need to change the last record
- // key as a result of this change
-
- if (i==nclusrec-1 && parent) parent->newkey(clusn,newkey,clusn);
- return i;
- }
-